﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class provides the interpolation of a Newton polynomial.
    /// </summary>
    [Serializable]
    public class NewtonPolynomialInterpolation
    {
        /// <summary>
        /// The x values for the interpolation.
        /// </summary>
        private readonly double[] xValues;

        /// <summary>
        /// The y or f(x) values for the interpolation.
        /// </summary>
        private readonly double[] yValues;

        /// <summary>
        /// Initializes a new instance of the <see cref="NewtonPolynomialInterpolation"/> class.
        /// </summary>
        /// <param name="xValues">The x values for the interpolation.</param>
        /// <param name="yValues">The y or f(x) values for the interpolation.</param>
        public NewtonPolynomialInterpolation(double[] xValues, double[] yValues)
        {
            if (xValues == (double[]) ((object) null))
            {
                throw new ArgumentNullException("xValues");
            }

            if (yValues == (double[]) ((object) null))
            {
                throw new ArgumentNullException("yValues");
            }

            if (xValues.Length != yValues.Length)
            {
                throw new Exception("The number of x values has to be even with the number of y values.");
            }

            this.xValues = xValues;
            this.yValues = yValues;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NewtonPolynomialInterpolation"/> class.
        /// </summary>
        /// <param name="list">The value list for the interpolation.</param>
        public NewtonPolynomialInterpolation(Point2D[] list)
        {
            if (list == (Point2D[]) null)
            {
                throw new ArgumentNullException("list");
            }

            this.xValues = new double[list.Length];
            this.yValues = new double[list.Length];

            for (int i = 0; i < list.Length; i++)
            {
                this.xValues[i] = list[i].X;
                this.yValues[i] = list[i].Y;
            }
        }

        /// <summary>
        /// Gets the x values for the interpolation.
        /// </summary>
        /// <value>The x values for the interpolation.</value>
        public double[] XValues
        {
            get { return xValues; }
        }

        /// <summary>
        /// Gets the y values for the interpolation.
        /// </summary>
        /// <value>The y values for the interpolation.</value>
        public double[] YValues
        {
            get { return yValues; }
        }

        /// <summary>
        /// Interpolates the polynomial.
        /// </summary>
        /// <returns>The interpolated polynomial.</returns>
        public NewtonPolynomial InterpolatePolynomial()
        {
            if (this.xValues.Length == 2)
            {
                double[] c = new double[1];
                double[] x = new double[1];

                x[0] = this.xValues[0];
                c[0] = (this.yValues[1] - this.yValues[0]) / ((this.xValues[1] - this.xValues[0]));

                return new NewtonPolynomial(c, x, this.yValues[0]);
            }

            double[] workingValues = (double[]) this.yValues.Clone();

            for (int i = 1; i <= this.xValues.Length; i++)
            {
                for (int j = this.xValues.Length - 1; j >= i; j--)
                {
                    workingValues[j] = (workingValues[j] - workingValues[j - 1])
                                       / (xValues[j] - xValues[j - i]);
                }
            }

            //return new NewtonPolynomial(Arrays.Copy(workingValues, 1, workingValues.Length - 1),
            //                            Arrays.Copy(this.xValues, 0, this.xValues.Length - 2), this.yValues[0]);

            return new NewtonPolynomial(Arrays.Copy(workingValues, 1, workingValues.Length),
                                        Arrays.Copy(this.xValues, 0, this.xValues.Length - 1), this.yValues[0]);
        }
    }
}